package com.dg.river.netty.axle.net;



import com.dg.river.netty.axle.msg2dev.BaseMsg;
import com.dg.river.netty.axle.msg2dev.DevMsg;
import com.dg.river.netty.axle.msg2dev.DevMsgHead;
import com.dg.river.netty.axle.msg2dev.MessageConst;
import com.dg.river.netty.axle.msg2dev.Msg01_AuditSignIn;
import com.dg.river.netty.axle.msg2dev.Msg02_Return;
import com.dg.river.netty.axle.msg2dev.Msg05_Heartbeat;
import com.dg.river.netty.axle.msg2dev.Msg11_AxleSignIn;
import com.dg.river.netty.axle.msg2dev.Msg21_AxleData;
import com.dg.river.netty.axle.msg2dev.Msg22_AuditInfo;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.msgpack.jackson.dataformat.JsonArrayFormat;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.ReferenceCountUtil;

import static com.dg.river.netty.axle.msg2dev.MessageConst.MESSAGE_FLAG;
import static com.dg.river.netty.axle.msg2dev.MessageConst.MTYPE_RETURN;


public class DeviceMsgDecoder extends LengthFieldBasedFrameDecoder {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private static final int MAX_FRAMELENGTH = 5242880;
    private static final int LENGTH_FIELD_OFFSET = 2;
    private static final int LENGTH_FIELD_LENGTH = 4;
    private static final int LENGTH_ADJUSTMENT = 0;
    private static final int INITIAL_BYTESTOSTRIP = 0;

    public DeviceMsgDecoder() {
        super(MAX_FRAMELENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTESTOSTRIP);
    }


    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null) {
            return null;
        }

        //判断报文合法性，校验报文
        DevMsgHead header = new DevMsgHead();
        DevMsg message = new DevMsg();
        try {
            header.setMsgFlag(frame.readUnsignedByte());
            header.setVersion(frame.readUnsignedByte());
            if (header.getMsgFlag() != MESSAGE_FLAG) {
                logger.error(String.format("报文标志非法: %x, 关闭连接", header.getMsgFlag()));
                ctx.close();
            }
            //TODO 根据版本号处理（目前不同版本间报文头是一样的）
            header.setMsgLen(frame.readUnsignedInt());
            //开始计算CRC码
            int crc = CRC16.CRC16(frame, frame.readerIndex(), frame.readableBytes() - 2);
            //比较CRC码
            if (crc != frame.getUnsignedShort(frame.writerIndex() - 2)) {
                //CRC校验不通过
                logger.error("CRC in msg: {}, calc: {}",
                        frame.getUnsignedShort(frame.writerIndex() - 2),
                        crc);
                //TODO for debug
                //ctx.close();
            }
            //继续取数据
            header.setDeviceId(frame.readUnsignedInt());
            header.setMsgType(frame.readUnsignedByte());
            header.setMsgId(frame.readUnsignedByte());
            header.setTimestamp(new Date(frame.readUnsignedInt() * 1000));

            message.setHeader(header);
            switch (header.getMsgType()) {
                case MessageConst.MTYPE_AXLESIGNIN:
                    message.setBody(decodeMsgPackMsg(frame, Msg11_AxleSignIn.class));
                    break;
                case MessageConst.MTYPE_HEARTBEAT:
                    message.setBody(decodeMsgPackMsg(frame, Msg05_Heartbeat.class));
                    break;
                case MTYPE_RETURN:
                    message.setBody(decodeMsgPackMsg(frame, Msg02_Return.class));
                    break;
                case MessageConst.MTYPE_AXLEDATA:
                    message.setBody(decodeMsgPackMsg(frame, Msg21_AxleData.class));
                    break;
                case MessageConst.MTYPE_AUDITSIGNIN:
                    message.setBody(decodeMsgPackMsg(frame, Msg01_AuditSignIn.class));
                    break;
                case MessageConst.MTYPE_AUDITINFO:
                    message.setBody(decodeMsgPackMsg(frame, Msg22_AuditInfo.class));
                    break;
                default:
                    //非法报文
                    break;

            }
        } finally {
            ReferenceCountUtil.release(frame);
        }

        if (message.getBody() == null) {
            logger.debug("解码失败, 回复非法报文，关闭链接");
            DevMsg retMsg = new DevMsg();
            DevMsgHead hh = new DevMsgHead();
            hh.setMsgFlag(MESSAGE_FLAG);
            hh.setVersion((short) 0x01);
            hh.setMsgType((short) MTYPE_RETURN);
            Msg02_Return msg02 = new Msg02_Return();
            msg02.setResult(MessageConst.RETURN_ERROR);
            msg02.setErrCode(MessageConst.ERRCODE_INVALIDMSG);
            msg02.setErrMsg("非法报文");
            retMsg.setBody(msg02);
            ctx.writeAndFlush(retMsg).addListener(ChannelFutureListener.CLOSE);
            return null;
        }
        return message;
    }

    private <T> T decodeMsgPackMsg(ByteBuf frame, Class<T> c) {
        //messagepack解包数据
        ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
        objectMapper.setAnnotationIntrospector(new JsonArrayFormat());
        //去掉CRC，其余为整包数据
        int nlen = frame.readableBytes() - 2;
        byte[] ary = new byte[nlen];
        frame.readBytes(ary, 0, nlen);
        T msgRet = null;
        try {
            msgRet = objectMapper.readValue(ary, c);
            if (msgRet instanceof BaseMsg) {
                ((BaseMsg) msgRet).afterPack();
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("解析报文(类型：)时出现错误:{}}", e);
            return null;
        }
        return msgRet;
    }
}
